At first I used HTODemux for Day 3 and MULTIseqDemux for Day 15 because the ridge plots had better separation at Day 3 that way. However, we get 1664 more singlets with MULTIseqDemux at Day 3 and the hashtag assignment is almost entirely in agreement between the two tools. So I’ve decided to use MULTIseqDemux for both timepoint for consistency.
library(Seurat)
library(tidyverse)
library(knitr)
library(cowplot)
library(ggplot2)
set.seed(4)
script_output_dir <- file.path(here::here(), "output")
# Create folders for data
if(!dir.exists(file.path(script_output_dir))) {
cat(sprintf("Creating folder %s\n", file.path(script_output_dir, "processed_data")))
dir.create(file.path(script_output_dir, "processed_data"), recursive = T)
cat(sprintf("Creating folder %s\n", file.path(script_output_dir, "plots")))
dir.create(file.path(script_output_dir, "plots"), recursive = T)
}
Load data into Seurat objects
# Linux
day3_f <- "/media/emmabishop/5TBSharedStorage/project_data/2022_BCGChallenge/Round1/20230323_Day3_aggr/outs/count/filtered_feature_bc_matrix.h5"
day15_f <- "/media/emmabishop/5TBSharedStorage/project_data/2022_BCGChallenge/Round1/20230207_Day15/per_sample_outs/20230203_Seshadri_UW/count/sample_filtered_feature_bc_matrix.h5"
Make objects
make_seurat_obj <- function(infile, projectname) {
dat <- Read10X_h5(infile)
# Remove ".1" from gene names
rownames(dat$`Antibody Capture`) <- rownames(dat$`Antibody Capture`) %>%
str_replace(., fixed(".1"), "")
# Extract matrices of ADT and HTO info
ADT <- !grepl("Hashtag", dat$`Antibody Capture`@Dimnames[[1]])
ADT_counts <- dat$`Antibody Capture`[ADT,]
HTO <- grepl("Hashtag", dat$`Antibody Capture`@Dimnames[[1]])
HTO_counts <- dat$`Antibody Capture`[HTO,]
# Make object
obj <- CreateSeuratObject(counts = dat$`Gene Expression`, project = projectname, min.cells = 3)
obj[["ADT"]] <- CreateAssayObject(counts = ADT_counts)
obj[["HTO"]] <- CreateAssayObject(counts = HTO_counts)
Assays(obj)
return(obj)
}
day3 <- make_seurat_obj(day3_f, "Day3")
day15 <- make_seurat_obj(day15_f, "Day15")
# Starting counts
d3_start_count <- length(day3$orig.ident)
d15_start_count <- length(day15$orig.ident)
Raw HTO counts for each day
rowSums(day3$HTO) %>%
as.data.frame() %>%
kable(align = "l", caption = "Day 3")
| . | |
|---|---|
| Hashtag1 | 805481 |
| Hashtag2 | 1928906 |
| Hashtag3 | 1490418 |
| Hashtag4 | 1953263 |
| Hashtag5 | 1019046 |
| Hashtag6 | 3034373 |
| Hashtag7 | 5928 |
| Hashtag8 | 791903 |
| Hashtag9 | 2986667 |
| Hashtag10 | 2705637 |
rowSums(day15$HTO) %>%
as.data.frame() %>%
kable(align = "l", caption = "Day 15")
| . | |
|---|---|
| Hashtag1 | 4067421 |
| Hashtag3 | 1405752 |
| Hashtag4 | 204878 |
| Hashtag5 | 3176998 |
| Hashtag6 | 1102579 |
| Hashtag7 | 8374125 |
| Hashtag9 | 3458347 |
| Hashtag10 | 2079834 |
Normalize data
day3_norm <- NormalizeData(day3, assay = "HTO", normalization.method = "CLR", margin = 2)
## Normalizing across cells
day15_norm <- NormalizeData(day15, assay = "HTO", normalization.method = "CLR", margin = 2)
## Normalizing across cells
HTO distributions for each day
hto_distr <- function(inobj) {
HTO_counts <- inobj$HTO@data
to_plot <- HTO_counts %>%
as.data.frame() %>%
rownames_to_column("hashtag") %>%
pivot_longer(!hashtag) %>%
select(-c(name))
h <- ggplot(to_plot, aes(value)) +
geom_histogram() +
theme_classic() +
theme(axis.title.x = element_blank(),
axis.title.y = element_blank()) +
facet_wrap(~hashtag, scales = "free")
return(h)
}
Day 3
d3_hto <- hto_distr(day3_norm)
d3_hto + ggtitle("Day 3")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Day 15
d15_hto <- hto_distr(day15_norm)
d15_hto + ggtitle("Day 15")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
demux_multiseq <- function(norm_obj) {
# Demultiplex with MULTIseqDemux
dm <- MULTIseqDemux(norm_obj, assay = "HTO", autoThresh = TRUE)
i1 <- grepl("Hashtag", dm@meta.data$MULTI_ID)
i2 <- grepl("Doublet", dm@meta.data$MULTI_ID)
i3 <- grepl("Negative", dm@meta.data$MULTI_ID)
# Get overall Singlet, Doublet, Negative classification
dm@meta.data$MULTI_ID_global <- NULL
dm@meta.data$MULTI_ID_global[i1] <- "Singlet"
dm@meta.data$MULTI_ID_global[i2] <- "Doublet"
dm@meta.data$MULTI_ID_global[i3] <- "Negative"
dm@meta.data$MULTI_ID_global <- factor(dm@meta.data$MULTI_ID_global,
levels = c("Doublet",
"Negative",
"Singlet"))
return(dm)
}
day3_dm <- demux_multiseq(day3_norm)
day3_dm@meta.data$MULTI_ID <- factor(day3_dm@meta.data$MULTI_ID,
levels = c("Hashtag1", "Hashtag2", "Hashtag3",
"Hashtag4", "Hashtag5", "Hashtag6",
"Hashtag7", "Hashtag8", "Hashtag9",
"Hashtag10", "Doublet", "Negative"))
day15_dm <- demux_multiseq(day15_norm)
day15_dm@meta.data$MULTI_ID <- factor(day15_dm@meta.data$MULTI_ID,
levels = c("Hashtag1", "Hashtag3",
"Hashtag4", "Hashtag5", "Hashtag6",
"Hashtag7", "Hashtag9",
"Hashtag10", "Doublet", "Negative"))
Day 3
Missing hashtags:
table(day3_dm$MULTI_ID_global)
##
## Doublet Negative Singlet
## 1583 1828 13053
table(day3_dm$MULTI_ID)
##
## Hashtag1 Hashtag2 Hashtag3 Hashtag4 Hashtag5 Hashtag6 Hashtag7 Hashtag8
## 762 1250 1257 1612 1423 1404 5 1461
## Hashtag9 Hashtag10 Doublet Negative
## 1634 2245 1583 1828
Ridge plot using MULTI_ID
Idents(day3_dm) <- "MULTI_ID"
RidgePlot(day3_dm, assay = "HTO", ncol = 3,
features = c("Hashtag1", "Hashtag2", "Hashtag3", "Hashtag4", "Hashtag5",
"Hashtag6", "Hashtag8", "Hashtag9", "Hashtag10"))
Day 15
Missing hashtags:
table(day15_dm$MULTI_ID_global)
##
## Doublet Negative Singlet
## 466 484 5449
table(day15_dm$MULTI_ID)
##
## Hashtag1 Hashtag3 Hashtag4 Hashtag5 Hashtag6 Hashtag7 Hashtag9 Hashtag10
## 1191 16 9 1286 300 1442 730 475
## Doublet Negative
## 466 484
Ridge plot using MULTI_ID
Idents(day15_dm) <- "MULTI_ID"
RidgePlot(day15_dm, assay = "HTO", ncol = 3,
features = c("Hashtag1", "Hashtag3", "Hashtag4", "Hashtag5",
"Hashtag6", "Hashtag7", "Hashtag9", "Hashtag10"))
label <- function(dm) {
i1 <- grepl("^Hashtag1$", dm@meta.data$MULTI_ID)
i2 <- grepl("Hashtag2$", dm@meta.data$MULTI_ID)
i3 <- grepl("^Hashtag3$", dm@meta.data$MULTI_ID)
i4 <- grepl("^Hashtag4$", dm@meta.data$MULTI_ID)
i5 <- grepl("^Hashtag5$", dm@meta.data$MULTI_ID)
i6 <- grepl("^Hashtag6$", dm@meta.data$MULTI_ID)
i7 <- grepl("^Hashtag7$", dm@meta.data$MULTI_ID)
i8 <- grepl("^Hashtag8$", dm@meta.data$MULTI_ID)
i9 <- grepl("^Hashtag9$", dm@meta.data$MULTI_ID)
i10 <- grepl("^Hashtag10$", dm@meta.data$MULTI_ID)
i11 <- grepl("Doublet", dm@meta.data$MULTI_ID)
i12 <- grepl("Negative", dm@meta.data$MULTI_ID)
# PTID number
dm@meta.data$PTID <- NULL
dm@meta.data$PTID[i1] <- "1"
dm@meta.data$PTID[i2] <- "5"
dm@meta.data$PTID[i3] <- "7"
dm@meta.data$PTID[i4] <- "8"
dm@meta.data$PTID[i5] <- "9"
dm@meta.data$PTID[i6] <- "10"
dm@meta.data$PTID[i7] <- "11"
dm@meta.data$PTID[i8] <- "12"
dm@meta.data$PTID[i9] <- "13"
dm@meta.data$PTID[i10] <- "16"
dm@meta.data$PTID[i11] <- "Doublet"
dm@meta.data$PTID[i12] <- "Negative"
dm@meta.data$PTID <- factor(dm@meta.data$PTID,
levels = c("1", "5", "7", "8", "9", "10", "11",
"12", "13", "16", "Doublet", "Negative"))
# PTID sex
dm@meta.data$SEX <- NULL
dm@meta.data$SEX[i1] <- "M"
dm@meta.data$SEX[i2] <- "F"
dm@meta.data$SEX[i3] <- "I"
dm@meta.data$SEX[i4] <- "M"
dm@meta.data$SEX[i5] <- "F"
dm@meta.data$SEX[i6] <- "M"
dm@meta.data$SEX[i7] <- "M"
dm@meta.data$SEX[i8] <- "M"
dm@meta.data$SEX[i9] <- "M"
dm@meta.data$SEX[i10] <- "F"
dm@meta.data$SEX[i11] <- "Doublet"
dm@meta.data$SEX[i12] <- "Negative"
# PTID age
dm@meta.data$AGE <- NULL
dm@meta.data$AGE[i1] <- 29
dm@meta.data$AGE[i2] <- 40
dm@meta.data$AGE[i3] <- 40
dm@meta.data$AGE[i4] <- 41
dm@meta.data$AGE[i5] <- 36
dm@meta.data$AGE[i6] <- 28
dm@meta.data$AGE[i7] <- 36
dm@meta.data$AGE[i8] <- 43
dm@meta.data$AGE[i9] <- 28
dm@meta.data$AGE[i10] <- 20
dm@meta.data$AGE[i11] <- 0
dm@meta.data$AGE[i12] <- 0
# Arm metadata
dm@meta.data$ARM <- NULL
dm@meta.data$ARM[i1] <- "Non-INH"
dm@meta.data$ARM[i2] <- "Non-INH"
dm@meta.data$ARM[i3] <- "Non-INH"
dm@meta.data$ARM[i4] <- "Non-INH"
dm@meta.data$ARM[i5] <- "INH"
dm@meta.data$ARM[i6] <- "INH"
dm@meta.data$ARM[i7] <- "INH"
dm@meta.data$ARM[i8] <- "Non-INH"
dm@meta.data$ARM[i9] <- "INH"
dm@meta.data$ARM[i10] <- "INH"
dm@meta.data$ARM[i11] <- "Doublet"
dm@meta.data$ARM[i12] <- "Negative"
dm@meta.data$ARM <- factor(dm@meta.data$ARM, levels = c("Non-INH", "INH"))
return(dm)
}
day3_labelled <- label(day3_dm)
day15_labelled <- label(day15_dm)
saveRDS(day3_labelled, file = file.path(script_output_dir, "processed_data/1_dm_all_d3.rds"))
saveRDS(day15_labelled, file = file.path(script_output_dir, "processed_data/1_dm_all_d15.rds"))
Idents(day3_labelled) <- "MULTI_ID_global"
v1 <- VlnPlot(day3_labelled, features = "nCount_RNA", pt.size = 0.1, log = TRUE) +
ylim(0, 40000) +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Day 3 nCount_RNA") +
NoLegend()
## Warning: Default search for "data" layer in "RNA" assay yielded no results;
## utilizing "counts" layer instead.
Idents(day15_labelled) <- "MULTI_ID_global"
v2 <- VlnPlot(day15_labelled, features = "nCount_RNA", pt.size = 0.1, log = TRUE) +
ylim(0, 40000) +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Day 15 nCount_RNA") +
NoLegend()
## Warning: Default search for "data" layer in "RNA" assay yielded no results;
## utilizing "counts" layer instead.
cowplot::plot_grid(v1, v2)
## Warning: Removed 198 rows containing non-finite values (`stat_ydensity()`).
## Warning: Removed 198 rows containing missing values (`geom_point()`).
v3 <- VlnPlot(day3_labelled, features = "nCount_HTO", pt.size = 0.1, log = TRUE) +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Day 3 nCount_HTO") +
NoLegend()
## Warning: Default search for "data" layer in "RNA" assay yielded no results;
## utilizing "counts" layer instead.
v4 <- VlnPlot(day15_labelled, features = "nCount_HTO", pt.size = 0.1, log = TRUE) +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Day 15 nCount_HTO") +
NoLegend()
## Warning: Default search for "data" layer in "RNA" assay yielded no results;
## utilizing "counts" layer instead.
cowplot::plot_grid(v3, v4)
v5 <- VlnPlot(day3_labelled, features = "nCount_ADT", pt.size = 0.1, log = TRUE) +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Day 3 nCount_ADT") +
NoLegend()
## Warning: Default search for "data" layer in "RNA" assay yielded no results;
## utilizing "counts" layer instead.
v6 <- VlnPlot(day15_labelled, features = "nCount_ADT", pt.size = 0.1, log = TRUE) +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Day 15 nCount_ADT") +
NoLegend()
## Warning: Default search for "data" layer in "RNA" assay yielded no results;
## utilizing "counts" layer instead.
cowplot::plot_grid(v5, v6)
Get singlets
Idents(day3_labelled) <- "MULTI_ID_global"
Idents(day15_labelled) <- "MULTI_ID_global"
d3_singlet <- subset(day3_labelled, idents = "Singlet")
d15_singlet <- subset(day15_labelled, idents = "Singlet")
# Get singlet by HTO counts
d3_hto_singlet_count <- length(d3_singlet$orig.ident)
d15_hto_singlet_count <- length(d15_singlet$orig.ident)
# Start QC barplot dataframe
d3_qc_counts <- data.frame(d3_start_count, d3_hto_singlet_count)
d15_qc_counts <- data.frame(d15_start_count, d15_hto_singlet_count)
# These are necessary for the next script
saveRDS(d3_qc_counts, file = file.path(script_output_dir, "processed_data/2_d3_qc_df.rds"))
saveRDS(d15_qc_counts, file = file.path(script_output_dir, "processed_data/2_d15_qc_df.rds"))
saveRDS(d3_singlet, file = file.path(script_output_dir, "processed_data/2_dm_singlets_d3.rds"))
saveRDS(d15_singlet, file = file.path(script_output_dir, "processed_data/2_dm_singlets_d15.rds"))
df_day3 <- table(d3_singlet$orig.ident, d3_singlet$PTID) %>%
as.data.frame()
df_out <- table(d15_singlet$orig.ident, d15_singlet$PTID) %>%
as.data.frame() %>%
bind_rows(df_day3) %>%
filter(Var2 != "Doublet" & Var2 != "Negative") %>%
dplyr::rename("PTID" = "Var2") %>%
dplyr::rename("Timepoint" = "Var1") %>%
dplyr::rename("# Cells" = "Freq") %>%
mutate(Timepoint = as.factor(Timepoint))
df_out$Timepoint <- factor(df_out$Timepoint, levels = c("Day3", "Day15"))
# Barplot
ggplot(df_out, aes(x = PTID, y = `# Cells`, fill = Timepoint)) +
geom_bar(position = "dodge", stat = "identity", width = 0.7, color="black") +
theme_bw() +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
ggtitle("Number of Singlet Cells per PTID")
Number of Non-INH vs INH ptids and cells at each day
dfout2 <- df_out %>%
mutate(Arm = case_when(
PTID %in% c("1", "5", "7", "8", "12") ~ "Non-INH",
.default = "INH"
)) %>%
group_by(Timepoint, Arm) %>%
mutate(`Total Cells` = sum(`# Cells`)) %>%
mutate(`# PTIDs with > 20 cells` = case_when(
Timepoint == "Day15" & Arm == "Non-INH" ~ "n = 1",
Timepoint == "Day15" & Arm == "INH" ~ "n = 5",
Timepoint == "Day3" & Arm == "Non-INH" ~ "n = 5",
.default = "n = 4"
)) %>%
ungroup()
ggplot(dfout2, aes(x = Timepoint, y = `Total Cells`, fill = Arm)) +
geom_bar(position = "dodge", stat = "identity", width = 0.7) +
theme_bw() +
theme(text = element_text(size = 16),
axis.text = element_text(size = 16)) +
scale_fill_manual(values = c("orange", "blue4")) +
geom_text(aes(label=`# PTIDs with > 20 cells`),
position=position_dodge(width = 0.7),
vjust=-0.25) +
ggtitle("Total Singlets per Arm (n is # PTIDs with > 20 cells)")
dfout2 %>%
group_by(Timepoint, Arm, `# Cells`, PTID) %>%
tally() %>%
select(-c(n)) %>%
kable(align = "l", caption = "Singlets per Timepoint, Arm, PTID")
| Timepoint | Arm | # Cells | PTID |
|---|---|---|---|
| Day3 | INH | 5 | 11 |
| Day3 | INH | 1404 | 10 |
| Day3 | INH | 1423 | 9 |
| Day3 | INH | 1634 | 13 |
| Day3 | INH | 2245 | 16 |
| Day3 | Non-INH | 762 | 1 |
| Day3 | Non-INH | 1250 | 5 |
| Day3 | Non-INH | 1257 | 7 |
| Day3 | Non-INH | 1461 | 12 |
| Day3 | Non-INH | 1612 | 8 |
| Day15 | INH | 300 | 10 |
| Day15 | INH | 475 | 16 |
| Day15 | INH | 730 | 13 |
| Day15 | INH | 1286 | 9 |
| Day15 | INH | 1442 | 11 |
| Day15 | Non-INH | 0 | 5 |
| Day15 | Non-INH | 0 | 12 |
| Day15 | Non-INH | 9 | 8 |
| Day15 | Non-INH | 16 | 7 |
| Day15 | Non-INH | 1191 | 1 |
dfout2 %>%
group_by(Timepoint, Arm, `Total Cells`, `# PTIDs with > 20 cells`) %>%
tally() %>%
select(-c(n)) %>%
kable(align = "l", caption = "Singlets per Timepoint, Arm")
| Timepoint | Arm | Total Cells | # PTIDs with > 20 cells |
|---|---|---|---|
| Day3 | INH | 6711 | n = 4 |
| Day3 | Non-INH | 6342 | n = 5 |
| Day15 | INH | 4233 | n = 5 |
| Day15 | Non-INH | 1216 | n = 1 |
Get Singlets and Doublets
day3_subset <- subset(day3_labelled, idents = "Negative", invert = TRUE)
day15_subset <- subset(day15_labelled, idents = "Negative", invert = TRUE)
tSNE embedding of the HTO data
tsne_hto <- function(dm_subset) {
DefaultAssay(dm_subset) <- "HTO"
dm_subset <- ScaleData(dm_subset, features = rownames(dm_subset),
verbose = FALSE)
dm_subset <- RunPCA(dm_subset, features = rownames(dm_subset), approx = FALSE)
dm_subset <- RunTSNE(dm_subset, dims = 1:8, perplexity = 100)
DimPlot(dm_subset)
return(dm_subset)
}
day3_tsne <- tsne_hto(day3_subset)
day15_tsne <- tsne_hto(day15_subset)
p1 <- DimPlot(day3_tsne) + ggtitle("Day 3")
p2 <- DimPlot(day15_tsne) + ggtitle("Day 15")
p1 + p2
ptid_colors <- c("1" = "salmon", "5" = "orange", "7" = "#7570B3",
"8" = "#0CB702", "9" = "#A6761D", "10" = "#00BFC4",
"11" = "#C77CFF", "12" = "#FF61CC", "13" = "darkgreen",
"16" = "darkblue")
# Get tSNE singlets
d3_tsne_singlet <- subset(day3_tsne, idents = "Singlet")
d15_tsne_singlet <- subset(day15_tsne, idents = "Singlet")
Idents(d3_tsne_singlet) <- "PTID"
Idents(d15_tsne_singlet) <- "PTID"
p3 <- DimPlot(d3_tsne_singlet, cols = ptid_colors) + ggtitle("Day 3")
p4 <- DimPlot(d15_tsne_singlet, cols = ptid_colors) + ggtitle("Day 15")
p3 + p4
sessionInfo()
## R version 4.3.3 (2024-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: America/Los_Angeles
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] cowplot_1.1.1 knitr_1.45 lubridate_1.9.3 forcats_1.0.0
## [5] stringr_1.5.0 dplyr_1.1.3 purrr_1.0.2 readr_2.1.5
## [9] tidyr_1.3.1 tibble_3.2.1 ggplot2_3.4.4 tidyverse_2.0.0
## [13] Seurat_5.0.1 SeuratObject_5.0.1 sp_2.1-1
##
## loaded via a namespace (and not attached):
## [1] RColorBrewer_1.1-3 rstudioapi_0.15.0 jsonlite_1.8.7
## [4] magrittr_2.0.3 ggbeeswarm_0.7.2 spatstat.utils_3.0-4
## [7] farver_2.1.1 rmarkdown_2.25 vctrs_0.6.4
## [10] ROCR_1.0-11 spatstat.explore_3.2-5 htmltools_0.5.7
## [13] sass_0.4.7 sctransform_0.4.1 parallelly_1.36.0
## [16] KernSmooth_2.23-22 bslib_0.5.1 htmlwidgets_1.6.2
## [19] ica_1.0-3 plyr_1.8.9 plotly_4.10.3
## [22] zoo_1.8-12 cachem_1.0.8 igraph_1.5.1
## [25] mime_0.12 lifecycle_1.0.4 pkgconfig_2.0.3
## [28] Matrix_1.6-4 R6_2.5.1 fastmap_1.1.1
## [31] fitdistrplus_1.1-11 future_1.33.0 shiny_1.7.5.1
## [34] digest_0.6.33 colorspace_2.1-0 patchwork_1.1.3
## [37] rprojroot_2.0.3 tensor_1.5 RSpectra_0.16-1
## [40] irlba_2.3.5.1 labeling_0.4.3 progressr_0.14.0
## [43] fansi_1.0.6 spatstat.sparse_3.0-3 timechange_0.3.0
## [46] httr_1.4.7 polyclip_1.10-6 abind_1.4-5
## [49] compiler_4.3.3 here_1.0.1 bit64_4.0.5
## [52] withr_3.0.0 fastDummies_1.7.3 highr_0.10
## [55] MASS_7.3-60.0.1 tools_4.3.3 vipor_0.4.5
## [58] lmtest_0.9-40 beeswarm_0.4.0 httpuv_1.6.12
## [61] future.apply_1.11.0 goftest_1.2-3 glue_1.7.0
## [64] nlme_3.1-163 promises_1.2.1 grid_4.3.3
## [67] Rtsne_0.16 cluster_2.1.6 reshape2_1.4.4
## [70] generics_0.1.3 hdf5r_1.3.8 gtable_0.3.4
## [73] spatstat.data_3.0-3 tzdb_0.4.0 data.table_1.15.0
## [76] hms_1.1.3 utf8_1.2.4 spatstat.geom_3.2-7
## [79] RcppAnnoy_0.0.21 ggrepel_0.9.4 RANN_2.6.1
## [82] pillar_1.9.0 spam_2.10-0 RcppHNSW_0.5.0
## [85] later_1.3.1 splines_4.3.3 lattice_0.22-5
## [88] bit_4.0.5 survival_3.5-8 deldir_1.0-9
## [91] tidyselect_1.2.0 miniUI_0.1.1.1 pbapply_1.7-2
## [94] gridExtra_2.3 scattermore_1.2 xfun_0.40
## [97] matrixStats_1.0.0 stringi_1.8.3 lazyeval_0.2.2
## [100] yaml_2.3.8 evaluate_0.22 codetools_0.2-19
## [103] cli_3.6.2 uwot_0.1.16 xtable_1.8-4
## [106] reticulate_1.34.0 munsell_0.5.0 jquerylib_0.1.4
## [109] Rcpp_1.0.11 globals_0.16.2 spatstat.random_3.2-1
## [112] png_0.1-8 ggrastr_1.0.2 parallel_4.3.3
## [115] ellipsis_0.3.2 dotCall64_1.1-0 listenv_0.9.0
## [118] viridisLite_0.4.2 scales_1.3.0 ggridges_0.5.4
## [121] leiden_0.4.3 rlang_1.1.1